
/***********************************************************************
 * JCENTIPEDE
 * by Marc-Henri Vuillaume
 * *********************************************************************
 * This program is
 * free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 **********************************************************************/
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import java.util.Collection;
import java.util.Iterator;
import java.util.Random;
import java.util.Vector;
import mhv.fr.core.AppletGameLoop;
import mhv.fr.sprite.Sprite;
import mhv.fr.sprite.animator.SpriteAnimator;
import mhv.fr.sprite.container.SpriteContainer;
import mhv.fr.utils.RessourcesManager;
import mhv.fr.utils.Timer;

/**
 *
 * @author Marc-Henri
 */
public class JCentipede extends AppletGameLoop {

    /***********************************************************************
     * Applet Parameters
     ***********************************************************************/
    static int SCREENWIDTH = 800;
    static int SCREENHEIGHT = 600;
    static int PERIOD = 50; //Set the game to 50 Fps
    /*********************************************************************
     * Applet variables
     *********************************************************************/
    AffineTransform identity = new AffineTransform();
    /***********************************************************************
     * Game Parameters
     ***********************************************************************/
    static boolean SHOWINFOS = false;
    static boolean SHOWBOUNDINGRECT = false;
    static double CENTIPEDSPEED = 2.0;
    static int CENTIPEDELTSIZE = 18;
    static int CENTIPEDELTRADIUS = 9;
    static int BULLETSPEED = 10;
    static int SHIPSPEED = 2;
    static double SHIPSTARTX = SCREENWIDTH / 2;
    static double SHIPSTARTY = SCREENHEIGHT - 60;
    static int SHIPMAXHEIGHT = 460;
    static int SHIPMINHEIGHT = 580;
    static long RANDOMPERIOD = 1000; //time between to random events
    static int SPIDERSIZE = 10;
    static int SPIDERMAXHEIGHT = 590;
    static int SPIDERMINHEIGHT = 450;
    static double SPIDERSPEED = 1.0;
    static int MUSHROOMSIZE = 32;
    /*********************************************************************
     * Define Sprite types
     *********************************************************************/
    final int SPRITE_SHIP = 1;
    final int SPRITE_BULLET = 2;
    final int SPRITE_MUSHROOM_NORMAL = 10;
    final int SPRITE_MUSHROOM_HIT_1 = 11;
    final int SPRITE_MUSHROOM_HIT_2 = 12;
    final int SPRITE_CENTIPEDE_HEAD = 20;
    final int SPRITE_CENTIPED_BODY = 21;
    final int SPRITE_SPIDER = 30;
    /*********************************************************************
     * Game variables
     *********************************************************************/
    final int GAME_START = 0;
    final int GAME_RUN = 1;
    final int GAME_OVER = 2;
    Random rand = new Random();
    Timer randomTimer = new Timer();
    //Keyboard Keys
    boolean keyUp = false;
    boolean keyDown = false;
    boolean keyLeft = false;
    boolean keyRight = false;
    boolean keyFire = false;
    //the game entities
    long shipId;
    long bulletId;
    long spiderId;
    double[] xpos = new double[20];
    double[] ypos = new double[20];

    public JCentipede() {
        super(SCREENWIDTH, SCREENHEIGHT, PERIOD);
    }

    /*********************************************************************
     * Applet Init method
     *********************************************************************/
    @Override
    public void gameInit() {
        //Init Gameloop elements  
        RessourcesManager.getInstance().setApplet(this);

        setStatus(GAME_START);

        
    } //Applet Init

    /*********************************************************************
     * Applet Update method
     *********************************************************************/
    @Override
    public void gameRender(Graphics2D g2d) {
        // draw background
        g2d.setTransform(identity);
        g2d.setPaint(Color.BLACK);
        g2d.fillRect(0, 0, getSize().width, getSize().height);
        if (getStatus() == GAME_START) {
            drawGameMenu(g2d);
        } else if (getStatus() == GAME_OVER) {
            drawGameOver(g2d);
        }

        super.gameRender(g2d);

        //draw bounding rectangle
        if (SHOWBOUNDINGRECT && isRunning()) {
            Collection<Sprite> list = getSpriteList();
            for (Iterator<Sprite> it = list.iterator(); it.hasNext();) {
                Sprite sprite = it.next();
                if (sprite.isAlive()) {
                    if (sprite.isCollided()) {
                        sprite.drawBounds(Color.RED);
                    } else {
                        sprite.drawBounds(Color.WHITE);
                    }
                }

            }
        } //if

        //write some debug informations
        if (SHOWINFOS && isRunning()) {
            showInformations(g2d);
        }
        //write score
        if (getStatus()==GAME_RUN) {
            drawScore(g2d);
        }

    }//Update

    /*********************************************************************
     * Listener keypressed method
     *********************************************************************/
    @Override
    public void keyPressedEvent(int keyCode) {

        switch (keyCode) {

            case KeyEvent.VK_ENTER:
                if (getStatus() == GAME_START) {
                    CreateGameEntities(0);
                    setStatus(GAME_RUN);
                    setRunning(true);
                } else if (getStatus() == GAME_OVER) {
                    setStatus(GAME_START);
                }
                break;

            case KeyEvent.VK_LEFT:
                keyLeft = true;
                break;

            case KeyEvent.VK_RIGHT:
                keyRight = true;
                break;

            case KeyEvent.VK_DOWN:
                keyDown = true;
                break;

            case KeyEvent.VK_UP:
                keyUp = true;
                break;

            case KeyEvent.VK_SPACE:
                keyFire = true;
                break;

            case KeyEvent.VK_B:
                SHOWBOUNDINGRECT = !SHOWBOUNDINGRECT;
                break;

            case KeyEvent.VK_I:
                SHOWINFOS = !SHOWINFOS;
                break;

            default:
                break;

        }

    }//keyPressed

    /*********************************************************************
     * Listener keypressed method
     *********************************************************************/
    @Override
    public void keyReleasedEvent(int keyCode) {

        switch (keyCode) {

            case KeyEvent.VK_LEFT:
                keyLeft = false;
                break;

            case KeyEvent.VK_RIGHT:
                keyRight = false;
                break;

            case KeyEvent.VK_DOWN:
                keyDown = false;
                break;

            case KeyEvent.VK_UP:
                keyUp = false;
                break;

            case KeyEvent.VK_SPACE:
                keyFire = false;
                break;

            default:
                break;
        }
    }//keyRealed

    private void drawGameMenu(Graphics2D g2d) {
        g2d.setTransform(identity);
        g2d.setFont(new Font("Verdana", Font.BOLD, 36));
        g2d.setColor(Color.darkGray);
        g2d.drawString("JCENTIPED", 252, 202);
        g2d.setColor(new Color(200, 30, 30));
        g2d.drawString("JCENTIPED", 250, 200);

        g2d.setFont(new Font("Arial", Font.BOLD, 24));
        g2d.setColor(Color.ORANGE);
        g2d.drawString("Press ENTER to start", 280, 570);
    }

    private void drawGameOver(Graphics2D g2d) {
        g2d.setTransform(identity);
        g2d.setFont(new Font("Verdana", Font.BOLD, 36));
        g2d.setColor(Color.darkGray);
        g2d.drawString("GAME OVER", 252, 202);
        g2d.setColor(new Color(200, 30, 30));
        g2d.drawString("GAME OVER", 250, 200);

        g2d.setFont(new Font("Arial", Font.BOLD, 24));
        g2d.setColor(Color.ORANGE);
        g2d.drawString("Press ENTER to start", 280, 570);
    }

    private void drawScore(Graphics2D g2d) {
        g2d.setTransform(identity);
        g2d.setFont(new Font("Verdana", Font.BOLD, 16));
        g2d.setColor(Color.YELLOW);

        g2d.drawString(""+GameManager.getInstance().getScore(), 700, 15);
    }

    /*******************************************************************
     * Game Methods
     *******************************************************************/
    private void showInformations(Graphics2D g2d) {
        g2d.setTransform(identity);
        g2d.setFont(new Font("Verdana", Font.PLAIN, 12));
        g2d.setColor(Color.WHITE);
        g2d.drawString("FPS: " + getFps(), 5, 10);
        g2d.drawString("Screen: " + SCREENWIDTH + "," + SCREENHEIGHT, 5, 20);
        g2d.drawString("Ship: " + Math.round(getSprite(shipId).getX()) + "," +
                Math.round(getSprite(shipId).getY()), 5, 30);
        g2d.drawString("Nb of Centipedes: " + GameManager.getInstance().getNumberOfCentipede(), 5, 40);
        g2d.drawString("Spider: " + Math.round(getSprite(spiderId).getX()) + "," +
                Math.round(getSprite(spiderId).getY()), 5, 50);

    }

    @Override
    public void gameUpdate() {
        super.gameUpdate();
        checkKeyboard();
        checkRandomEvent();
        if ((GameManager.getInstance().getNumberOfCentipede() == 0) && isRunning()) {
            setStatus(GAME_OVER);
            setRunning(false);
        }
    }

    private void checkRandomEvent() {
        if (randomTimer.getTime() > RANDOMPERIOD) {
            randomTimer.reset();

            int dice = rand.nextInt(100);

            //add a spider event 20% chance
            if (!getSprite(spiderId).isAlive() && (dice < 20)) {
                getSprite(spiderId).setAlive(true);
            }
        }
    }

    private boolean checkCollisionWithRect(Rectangle rec, Vector<Sprite> list) {
        boolean collided = false;
        Iterator<Sprite> itr = list.iterator();
        while (itr.hasNext() && !collided) {
            Sprite m = (Sprite) itr.next();
            if (m.isAlive() && m.checkcollision(rec)) {
                collided = true;
            }
        }

        return collided;
    }//checkCollisions

    private void checkKeyboard() {
        double x, y;

        x = getSprite(shipId).getX();
        y = getSprite(shipId).getY();

        if (keyUp) {
            if (y > SHIPMAXHEIGHT) {
                y = y - SHIPSPEED;
            }
        }

        if (keyDown) {
            if (y < SHIPMINHEIGHT) {
                y = y + SHIPSPEED;
            }
        }

        if (keyLeft) {
            if (x > 6.0) {
                x = x - SHIPSPEED;
            }
        }

        if (keyRight) {
            if (x < (SCREENWIDTH - 6.0)) {
                x = x + SHIPSPEED;
            }
        }

        if (keyFire) {
            Sprite bullet = getSprite(bulletId);
            if (!bullet.isAlive()) {
                bullet.setX(getSprite(shipId).getX());
                bullet.setY(getSprite(shipId).getY());
                bullet.setAlive(true);
                bullet.setVelY(-BULLETSPEED);
                bullet.setVelX(0);
            }
        }
        getSprite(shipId).setX(x);
        getSprite(shipId).setY(y);
//            }    
    }//checkKeyboard

    public void CreateGameEntities (int level) {
         Graphics2D g2d = getSurface();
         GameManager.getInstance().clear();
         SpriteContainer.getInstance().Clear();
         
        // Init game elements
        //**************************
        //create bullet
        Sprite bullet;
        bullet = new Sprite(g2d);
        bullet.loadImage("Images/bullet.png");
        bullet.setController(new BulletController());
        bullet.setAlive(false);
        bullet.setType(SPRITE_BULLET);
        bulletId = addSprite(bullet);

        //create ship
        Sprite ship;
        ship = new Sprite(g2d);
        ship.loadImage("Images/ship.png");
        ship.setX(SHIPSTARTX);
        ship.setY(SHIPSTARTY);
        ship.setType(SPRITE_SHIP);
        ship.setAlive(true);
        shipId = addSprite(ship);

        //create spider
        Sprite spider;
        spider = new Sprite(g2d);
        SpriteAnimator anim = new SpriteAnimator();
        anim.load("Images/spider.png", 5, 1, 34, 24);
        anim.setFrameDelay(10);
        spider.setController(new SpiderController());
        spider.setAnimator(anim);
        spider.setX(SCREENWIDTH / 2);
        spider.setY(SCREENHEIGHT / 2);
        spider.setType(SPRITE_SPIDER);
        spider.setAlive(false);
        spiderId = addSprite(spider);

        //create mushroom forest
        Vector<Sprite> forest = new Vector<Sprite>(30);
        for (int i = 0; i < 30; i++) {
            int x, y;
            int hDelta = SCREENHEIGHT - SHIPMAXHEIGHT;
            Sprite m = new Sprite(g2d);
            m.setType(SPRITE_MUSHROOM_NORMAL);
            m.setController(new MushroomController());
            m.loadImage("Images/mush_normal.png");
            do {
                x = rand.nextInt(SCREENWIDTH / MUSHROOMSIZE) * MUSHROOMSIZE + MUSHROOMSIZE / 2;
                y = CENTIPEDELTSIZE + rand.nextInt(
                        (SCREENHEIGHT - hDelta - CENTIPEDELTSIZE) / MUSHROOMSIZE) * MUSHROOMSIZE + MUSHROOMSIZE / 2;
                m.setX(x);
                m.setY(y);
                m.setAlive(true);
            } while (checkCollisionWithRect(m.getBounds(), forest));

            forest.add(m);
            addSprite(m);
        }


        //create centiped
        GameManager cpManager = GameManager.getInstance();
        Centipede cp = cpManager.CreateCentipede();
        for (int i = 0; i < 8; i++) {
            Sprite ce = cp.CreateElement(CENTIPEDELTSIZE, CENTIPEDELTRADIUS - i * CENTIPEDELTSIZE, g2d);
            anim = new SpriteAnimator();
            if (i == 0) {
                anim.load("Images/head.png", 4, 1, 22, 21);
            } else {
                anim.load("Images/body.png", 4, 1, 18, 21);
            }
            anim.setFrameDelay(5);
            ce.setAnimator(anim);
            ce.setAlive(true);

        }

        cp.setHStatus(Centipede.CentipedeStatus.RIGHT);
        cp.setVStatus(Centipede.CentipedeStatus.DOWN);

        //start timer
        randomTimer.start();

    }
}//JCentiped
